package org.hawk.gwt.ppc.parser.impl;

import java.util.List;
import java.util.Map;

import org.hawk.gwt.ppc.parser.AnnotationDescriptor;
import org.hawk.gwt.ppc.parser.Descriptor;
import org.hawk.gwt.ppc.parser.DescriptorVisitor;
import org.hawk.gwt.ppc.parser.ExpressionDescriptor;
import org.hawk.gwt.ppc.parser.JavaKeywords;
import org.hawk.gwt.ppc.parser.Token;
import org.hawk.gwt.ppc.utils.PreprocessorException;
import static org.hawk.gwt.ppc.parser.impl.DescriptorParserUtils.*;

/**
 * Parses annotations attacked to some object.
 * 
 * @author alex.bereznevatiy@gmail.com
 *
 */
final class AnnotationDescriptorParser implements DescriptorParser {

	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.parser.impl.DescriptorParser#accepts(org.hawk.gwt.ppc.parser.Token)
	 */
	public boolean accepts(Token token) {
		return token.equalsTo('@');
	}

	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.parser.impl.DescriptorParser#parse(org.hawk.gwt.ppc.parser.impl.TokenBasedJavaParser, org.hawk.gwt.ppc.parser.impl.Tokenizer)
	 */
	public Descriptor parse(TokenBasedJavaParser parser, TokenSource tokenizer, Object...params)
			throws PreprocessorException {
		if (params.length == 0) {
			lookupNext(tokenizer, false);
		}
		
		return parseAnnotation(parser, tokenizer);
	}

	/**
	 * Parses annotation started from annotation name (token after @).
	 * @param parser
	 * @param tokenizer
	 * @return
	 * @throws PreprocessorException
	 */
	private AnnotationDescriptor parseAnnotation(TokenBasedJavaParser parser,
			TokenSource tokenizer) throws PreprocessorException {
		// TODO: implement, refactor with parameters support
		Token first = tokenizer.current();
		lookupNext(tokenizer, false);
		if (!tokenizer.current().equalsTo('(')) {
			return null;// no parameter
		}
		int deep = 1;
		while (deep > 0) {
			lookupNext(tokenizer, false);
			if (tokenizer.current().equalsTo('(')) {
				deep ++;
			} else if (tokenizer.current().equalsTo(')')) {
				deep --;
			}
		}
		Token last = tokenizer.current();
		lookupNext(tokenizer, false);
		return new AnnotationDescriptorImpl(first, last);
	}
	
	/**
	 * Parses annotations attacked to element.
	 * @param parser
	 * @param tokenizer
	 * @param annotations
	 * @return <code>true</code> if next element is an annotation type.
	 * @throws PreprocessorException
	 */
	static boolean parseAnnotations(TokenBasedJavaParser parser, 
			TokenSource tokenizer, List<AnnotationDescriptor> annotations) 
			throws PreprocessorException {
		if (!DescriptorType.ANNOTATION.accepts(tokenizer.current())) {
			return false;
		}
				
		do {
			lookupNext(tokenizer, false);
			if (tokenizer.current().equalsTo(JavaKeywords.INTERFACE)) {
				lookupNext(tokenizer, false);
				return true;
			}
			annotations.add((AnnotationDescriptor) DescriptorType.ANNOTATION.parse(parser, tokenizer, Boolean.TRUE));
		} while (tokenizer.current().equalsTo('@'));
		
		return false;
	}

	/**
	 * 
	 * @author alex.bereznevatiy@gmail.com
	 *
	 */
	private static final class AnnotationDescriptorImpl extends AbstractDescriptor implements AnnotationDescriptor {

		AnnotationDescriptorImpl(Token first, Token last) {
			super(first, last);
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.Descriptor#visit(org.hawk.gwt.ppc.parser.DescriptorVisitor)
		 */
		public void visit(DescriptorVisitor visitor) throws PreprocessorException {
			visitor.visit(this);
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.AnnotationDescriptor#getValues()
		 */
		public Map<String, ExpressionDescriptor> getValues() {
			throw new UnsupportedOperationException("Not implemented yet");
		}
		
	}
}
